/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufc.si.tcc.qmusic.util;

import br.ufc.si.tcc.qmusic.model.Nota;
/**
 *
 * @author Lucas Ismaily
 */
import java.util.ArrayList;
import java.util.Vector;

import jm.music.data.Note;
import jm.music.data.Part;
import jm.music.data.Phrase;

/*
 * Melody Extractor Pipeline
 *
 *
 *
 * We use skyline to discard the overlaped pitch
 *
 *
 *
 */
public class skyline {

    static private ArrayList<Note> melody = new ArrayList<Note>();
   
    public static void melodySort(int p, int r) {
        int q;
        if (p < r) {
            q = partition(p, r);

            melodySort(p, q - 1);
            melodySort(q + 1, r);
        }


    }

    public static int partition(int p, int r) {
        double x = melody.get(r).getSampleStartTime();
        int i = p - 1;

        for (int j = p; j < r; j++) {
            if (melody.get(j).getSampleStartTime() <= x) {
                i++;
                Note temp;

                Note temp2;

                temp = melody.get(i);
                temp2 = melody.get(j);

                melody.remove(j);
                melody.add(j, temp);
                melody.remove(i);
                melody.add(i, temp2);
            }


        }
        Note temp;
        Note temp2;
        temp = melody.get(i + 1);
        temp2 = melody.get(r);

        melody.remove(r);
        melody.add(r, temp);

        melody.remove(i + 1);
        melody.add(i + 1, temp2);


        return i + 1;
    }

    public static int CheckMelody(Note notes) {

        int low = 0;
        int high = melody.size() - 1;
        int mid = 0;
        double value = notes.getSampleStartTime();
        while (low <= high) {
            mid = (low + high) / 2;
            if (melody.get(mid).getSampleStartTime() > value) {
                high = mid - 1;
            } else {
                if (melody.get(mid).getSampleStartTime() < value) {
                    low = mid + 1;
                } else {
                    return mid;
                }
            }

        }
        return -1;

    }

    public static void initMelody() {
        melody = new ArrayList();
    }

    public static void skyline(Phrase phrase) {
       
        int pos;
        Note notes;
        for (int n = 0; n < phrase.size(); n++) {
            notes = phrase.getNote(n);

            if ((pos = CheckMelody(notes)) >= 0) {

                //if  smaller, discard else update
                if (notes.getPitch() > melody.get(pos).getPitch()) {
                    melody.remove(pos);
                    melody.add(pos, notes);
                }
            } //else add to tail
            else {
                melody.add(phrase.getNote(n));
            }
        }
        //sort the melody by Sample start time
        melodySort(0, melody.size() - 1);

    }

    public static Phrase OrderPhrase(ArrayList<Nota> pitches, Phrase phrase) {
      

        for (int n = 0; n < phrase.size(); n++) {
            if (phrase.getNote(n).getPitch() < 0) {
                phrase.removeNote(n);
            }
        }
        System.out.println(phrase.size());
        System.out.println(pitches.size());

        for (int n = 0; n < phrase.size(); n++) {
            phrase.getNote(n).setSampleStartTime(pitches.get(n).getDeltaTimeInicial());

        }

        return phrase;
    }

   
    public static double roundToNearestQuarter(double time) {

        double down;
        double rnum = time;
        down = java.lang.Math.floor(time);


        if (time > down && time < (down + 0.25)) {
            if (time < (down + (0.25 / 2))) {
                rnum = down;
            } else {
                rnum = down + 0.25;
            }
        }
        if (time > (down + 0.25) && time < (down + 0.50)) {
            if (time < ((down + 0.25) + (0.25 / 2))) {
                rnum = down + 0.25;
            } else {
                rnum = down + 0.50;
            }

        }
        if (time > (down + 0.50) && time < (down + 0.75)) {
            if (time < ((down + 0.50) + (0.25 / 2))) {
                rnum = down + 0.50;
            } else {
                rnum = down + 0.75;
            }

        }
        if (time > (down + 0.75) && time < (down + 1)) {
            if (time < ((down + 0.75) + (0.25 / 2))) {
                rnum = down + 0.75;
            } else {
                rnum = down + 1;
            }

        }

        return rnum;
    }

  
    public static ArrayList<Nota> OrderIntoSlot(Phrase phrase) {

        ArrayList<Nota> pitches = new ArrayList();

        double time = phrase.getStartTime();

        double lastAssignedTime = -1;
        double assignedTime = 0;
        Vector<Note> notes = phrase.getNoteList();

        for (int n = 0; n < notes.size(); n++) {
            Note N = notes.get(n);
            double noteStartTime = roundToNearestQuarter(time);

            while (noteStartTime + assignedTime < lastAssignedTime) {
                assignedTime += 0.25;
            }

            if (N.getPitch() > 0) {
                Nota ts1 = new Nota();
                ts1.setVolume(N.getPitch());
                ts1.setDeltaTimeInicial(noteStartTime + assignedTime);
                pitches.add(ts1);


            } else {
                while (noteStartTime + assignedTime < time + N.getDuration()) {
                    assignedTime += 0.25;
                }
            }

            lastAssignedTime = noteStartTime + assignedTime;
            time += N.getDuration();

        }
        return pitches;
    }

    public static Phrase RemoveLongDuration(Phrase phrase) {
        Phrase temp = new Phrase();
        temp.setStartTime(phrase.getStartTime());
        for (int n = 0; n < phrase.size(); n++) {
            if (phrase.getNote(n).getDuration() < 5) {
                temp.addNote(phrase.getNote(n));
            }
        }
        return temp;
    }

    public static ArrayList<Note> doSkyline(Part melodyChannel) {



        Vector phrase_list = melodyChannel.getPhraseList();
        for (int n = 0; n < phrase_list.size(); n++) {
            Phrase temp = (Phrase) phrase_list.get(n);
            temp = RemoveLongDuration(temp);
            temp = OrderPhrase(OrderIntoSlot(temp), temp);
            skyline(temp);
        }

        return melody;

    }
}